package com.wenwenli.boot.context;

import com.wenwenli.boot.annotation.Component;
import com.wenwenli.boot.annotation.Controller;
import com.wenwenli.boot.annotation.Service;
import com.wenwenli.boot.aop.ProxyFactoryBean;
import com.wenwenli.boot.beans.BeanDefinition;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author 写不完的BUG
 * @Description 应用容器上下文
 * @Date 2019-04-15
 **/
public class ApplicationContext {

    static class ApplicationContextHolder {
        static ApplicationContext applicationContext = new ApplicationContext();
    }

    public static ApplicationContext getInstance() {
        return ApplicationContextHolder.applicationContext;
    }

    private String SCAN_BASE_PACKAGE;

    private Map<String, BeanDefinition> beanMaps = new ConcurrentHashMap<>();

    private ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();

    private ApplicationContext() {
    }

    /**
     * 根据类从bean容器获取对象
     *
     * @param clazz
     * @return
     */
    public Object getBean(Class clazz) {
        Iterator<String> iterator = beanMaps.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            BeanDefinition beanDefinition = (BeanDefinition) beanMaps.get(key);
            if (clazz.isAssignableFrom(beanDefinition.getTarget().getClass())) {
                return beanDefinition.getTarget();
            }
        }
        return null;
    }

    public Map<String, BeanDefinition> getApplicationContextBeans() {
        return beanMaps;
    }

    /**
     * 初始化容器
     */
    protected ApplicationContext initContext(String basePackage) {
        SCAN_BASE_PACKAGE = basePackage;
        try {
            //获取扫描的类路径集合
            List<String> classPaths = getClassList();
            //填充bean容器
            parseBean(classPaths);
            //解析Autowired字段注入
            parseAutowired();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return this;

    }

    /**
     * 解析字段上的Autowired注解
     *
     * @throws IllegalAccessException
     */
    private void parseAutowired() throws IllegalAccessException {
        Iterator<String> iterator = beanMaps.keySet().iterator();
        while (iterator.hasNext()) {
            String beanName = iterator.next();
            BeanDefinition bean = (BeanDefinition) beanMaps.get(beanName);
            Field[] declaredFields = bean.getTarget().getClass().getDeclaredFields();
            for (int i = 0; i < declaredFields.length; i++) {
                Field field = declaredFields[i];
                field.setAccessible(true);
                field.set(bean.getTarget(), getBean(bean.getTarget().getClass()));
            }
        }
    }

    /**
     * 解析bean
     *
     * @param classPaths
     */
    private void parseBean(List<String> classPaths) {
        try {
            String classpath = ApplicationContext.class.getResource("/").getPath();
            for (String s : classPaths) {
                s = s.replace(classpath.replace("/", "\\").replaceFirst("\\\\", ""), "").replace("\\", ".").replace(".class", "");
                Class cls = Class.forName(s);
                if (cls.isAnnotationPresent(Component.class) || cls.isAnnotationPresent(Controller.class) || cls.isAnnotationPresent(Service.class)) {
                    //实现了接口则需要生成代理类
                    if (cls.getGenericInterfaces().length > 0) {
                        Object instance = cls.newInstance();
                        beanMaps.put(cls.getName(), new BeanDefinition(instance, proxyFactoryBean.getProxy(instance), getBeanName(cls)));
                    } else {
                        Object instance = cls.newInstance();
                        beanMaps.put(cls.getName(), new BeanDefinition(instance, null, getBeanName(cls)));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private String getBeanName(Class cls) {
        String beanName = "";
        if (cls.isAnnotationPresent(Controller.class)) {
            beanName = ((Controller) cls.getAnnotation(Controller.class)).value();
        }
        if (cls.isAnnotationPresent(Service.class)) {
            beanName = ((Service) cls.getAnnotation(Service.class)).value();
        }
        if (cls.isAnnotationPresent(Component.class)) {
            beanName = ((Component) cls.getAnnotation(Component.class)).value();
        }
        if (StringUtils.isBlank(beanName)) {
            beanName = cls.getName();
        }
        return beanName;
    }

    private void doPath(File file, List<String> classPaths) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f1 : files) {
                doPath(f1, classPaths);
            }
        } else {
            if (file.getName().endsWith(".class")) {

                classPaths.add(file.getPath());
            }
        }
    }


    private List<String> getClassList() {
        List<String> classPaths = new ArrayList<String>();
        String classpath = ApplicationContext.class.getResource("/").getPath();
        SCAN_BASE_PACKAGE = SCAN_BASE_PACKAGE.replace(".", File.separator);
        String searchPath = classpath + SCAN_BASE_PACKAGE;
        doPath(new File(searchPath), classPaths);
        return classPaths;
    }
}
